/**
 * @brief I2C driver implementation
 * @author Gilad Avidov
 */
#include <mcs51reg.h>
#include <stdio.h>
#include "i2c.h"
#include "common.h"
#include "sched.h"

#define DELAY       (1)

static void i2c_idle()
{
    SDA = 1;
    SCL = 1;
}

void i2c_init()
{
    i2c_idle();
}

/* @return SDA */
static bit i2c_clock_cycle()
{
    bit sda;
    SCL = 1;
    usleep(DELAY);
    sda = SDA;
    SCL = 0;
    return sda;
}

unsigned char i2c_send_byte(unsigned char byte)
{
    unsigned char timeout = 0xFF;
    bit ack = 0;
    char i;

    /* send MSB first */
    for (i = 7 ; i >= 0 ; --i) {
        SDA = (byte & BIT(i));
        usleep(DELAY);
        i2c_clock_cycle();
        usleep(DELAY);
    }

    /* wait for ack */
    SDA = 1; /* ensure SDA is one */
    SCL = 1;
    for (timeout=0xFF ; timeout && !ack   ; --timeout)
        ack = !SDA;
    /* NACK */
    SCL = 0;
    usleep(DELAY);
    if (!ack)
        printf("sent byte:0x%x nack\n\r",byte);
    return !ack; /* zero on success */
}

void i2c_recv_byte(xdata unsigned char *byte, bit is_last)
{
    char i;
    bit _bit;
    /* send MSB first */
    for (i=7, *byte=0 ; i > -1 ; --i) {
        usleep(DELAY);
        _bit = i2c_clock_cycle();
        *byte <<= 1;
        *byte |= _bit;
    }
    usleep(DELAY);
    /* ACK / NACK */
    SDA = is_last;
    i2c_clock_cycle();
    usleep(DELAY);
}


void i2c_stop()
{
    SDA = 0;
    usleep(DELAY);
    SCL = 1;
    usleep(DELAY);
    SDA = 1;
}

unsigned char i2c_send_address(unsigned char seven_bit_addr, i2c_op_t op)
{
    /* start */
    SDA = 1;
    usleep(DELAY);
    SCL = 1;
    usleep(DELAY);
    SDA = 0;
    usleep(DELAY);
    SCL = 0;

    return i2c_send_byte(seven_bit_addr << 1 | (op ? 1 : 0));
}

void i2c_reset()
{
    unsigned char i;
    /* start */
    SDA = 1;
    usleep(DELAY);
    SCL = 1;
    usleep(DELAY);
    SDA = 0;
    usleep(DELAY);
    SCL = 0;
    usleep(DELAY);

    for (i=0; i<9 ; ++i) {
        i2c_clock_cycle();
        usleep(DELAY);
    }

    /* start */
    SDA = 1;
    usleep(DELAY);
    SCL = 1;
    usleep(DELAY);
    SDA = 0;
    usleep(DELAY);
    /* stop */
    SDA = 1;
}
